package com.suns.util;

import com.google.common.base.Preconditions;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.suns.exception.ParamException;
import org.apache.commons.collections.MapUtils;

import javax.validation.ConstraintViolation;
import javax.validation.Validation;
import javax.validation.Validator;
import javax.validation.ValidatorFactory;
import java.util.*;

/**
 * 自定义的校验类
 *
 * http://tool.oschina.net/apidocs/apidoc?api=guava    开源中国有关guava的帮助文档
 *     Maps  ,Lists   ,Preconditions
 */
public class BeanValidator {
    //1.调用Validation的静态方法buildDefaultValidatorFactory()获取校验工厂对象ValidatorFactory
    //全局校验工厂
    private static ValidatorFactory validatorFactory = Validation.buildDefaultValidatorFactory();

    /*
     泛型方法：  返回值类型前添加所有的参数
     Class... 可变的参数（数组）
     */

    /**
     *校验单个对象
     * @param t
     * @param groups
     * @param <T>
     * @return
     */
    public static <T> Map<String,String> validate(T t , Class... groups ) {

        //2-1调用校验工厂对象的实例方法getValidator()获取校验对象Validator
        Validator validator = validatorFactory.getValidator();

        //2-2调用校验对象Validator的实例方法validate(T,Class...) 获取校验结果 Set<ConstraintViolation>
        Set validateResult = validator.validate(t, groups);

        //2-3判断校验结果Set集合是否为空，空则代表验证成功
        if (validateResult.isEmpty()){

            /**
             * 单例模式  工厂模式 ？
             * http://www.runoob.com/design-pattern/design-pattern-tutorial.html
             */
            //2-3-1 由于没有错误信息，所以返回一个空的Map集合
            return Collections.emptyMap();

        }else {

            /*
              HashMap  LinkedHashMap   ?
              http://www.runoob.com/w3cnote/java-collections.html
             */
            //2-3-2如果非空 代表有些字段的校验出现问题
            //2-3-2.1创建LinkedHashMap对象存储错误字段，及错误信息
            LinkedHashMap erros = Maps.newLinkedHashMap();

            //2-3-2.2调用校验结果Set集合的实例方法iterator()，获取校验结果的迭代器对象
            Iterator it=validateResult.iterator();

            //2-3-2.3遍历迭代器对象
            while (it.hasNext()){

                //2-3-2.4获取迭代器对象内的每一个校验结果ConstraintViolation
                ConstraintViolation violation = (ConstraintViolation) it.next();

                //2-3-2.5将有问题的字段（ConstraintViolation对象的实例方法getPropertyPath() ）转成String字符串（toString()方法），及错误信息（ConstraintViolation对象的实例方法getMessag()）存入LinkedHashMsp对象内
                erros.put(violation.getPropertyPath().toString(), violation.getMessage());
            }

            //2-3-2.6返回含有错误细信息的LinkedHashMap集合
            return erros;
        }
    }

    /**
     *校验多个对象
     * @param collection
     * @return
     */
    public static Map<String,String> validateList(Collection<?> collection) {

        //3-1调用PreConditions类的静态方法checkNotNull(T) 来判断传递的校验集合是否非空  ，如果传递的集合为空，则会抛出异常
        Preconditions.checkNotNull(collection);

        //3-2否则传递的集合对象collection非空，调用该对象的iterator
        Iterator it = collection.iterator();

        //3-3声明校验结果 Map集合
        Map erros;

        //3-4循环判断校验结果，Map集合对象，是否为空，非空代表校验项有问题，停止校验
        do {

            //3-5-1判断校验迭代器 ，Iterator对象中 ，是否还有下一个对象
            if (!it.hasNext()){

                //3-5-1.1校验迭代器,Iterator对象中没有下一个对象 且 校验结果，Map集合对象，为空   即没有字段检验出错，返回空Map集合。
                return Collections.emptyMap();
            }

            //3-5-2.1 Iterator对象中有下一个对象， 获取迭代器中的每一个对象
            Object obj = it.next();

            //3-5-2.2 调用本类的校验方法validate(T,Class...) 或获得一个校验结果集合 Map对象 ，将他的引用指向  erros
            erros = validate(obj, new Class[0]);

        } while (erros.isEmpty());

        //3-6 返回校验结果 Map集合
        return  erros;
    }

    /**
     * 校验对象,选用合适的方法
     * @param first
     * @param objects
     * @return
     */
    public static Map<String,String> validateObject(Object first, Object... objects) {

        //判断附加对象数组是否非空 ，长度是否大于0  （即传来的校验对象有多个）
        if (objects!=null && objects.length > 0){

            //使用Lists的静态方法 asList(第一个对象Object，多个对象数组)整合成集合  ，调用校验多个对象集合的方法validateList(Collection)  ，将校验结果集合Map返回
            return validateList(Lists.asList(first,objects));
        } else {

            //调用校验单个对象方法validateList(T，Class...) 将校验结果集合Map返回
            return validate(first, new Class[0]);
        }
    }


    public static  void  check(Object obj) {
        Map<String , String> map=validateObject(obj);
        //使用MapUtils类中的方法判断Map是否为空 （map != null && map.keySet().size()>0）
        if (MapUtils.isNotEmpty(map)) {
            throw new ParamException(map.toString());
        }
    }
}
